/* =========================================================
   Aahana Surya — deep space
   Launch from the pad, make orbit, then head out.

   Type: an editorial pairing. A high-contrast serif carries the display
   voice; a neutral grotesque does the reading; a mono handles the small
   technical labels. Nothing decorative.
   ========================================================= */

:root {
    /* ---- ink ---------------------------------------------------------
       Near-neutral greys rather than blue-tinted text. The blue is an
       accent now, not the default colour of everything. */
    --ink-00: #ffffff;
    --ink-10: #eef1f6;
    --ink-40: #a7afbd;
    --ink-60: #848e9f;
    --ink-80: #4a5464;

    --fg: var(--ink-10);
    --fg-soft: var(--ink-40);
    --fg-faint: var(--ink-60);

    /* ---- ground ---- */
    --void: #05070f;
    --deep: #080d1c;

    /* ---- accent: the same sky blue, used sparingly ---- */
    --accent: #8fcaff;
    --accent-deep: #3b86ff;

    /* ---- lines: hairlines, never heavy ---- */
    --line: rgba(255, 255, 255, 0.10);
    --line-strong: rgba(255, 255, 255, 0.18);

    /* ---- type ---- */
    --font-display: 'Instrument Serif', 'Times New Roman', Times, serif;
    --font-body: 'Inter Tight', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;

    /* a modular scale, so nothing is sized by eye */
    --t-hero: clamp(3.2rem, 13vw, 11rem);
    --t-h2:   clamp(2.6rem, 7.5vw, 6rem);
    --t-h3:   clamp(1.3rem, 2.4vw, 1.9rem);
    --t-body: clamp(0.95rem, 1.15vw, 1.06rem);
    --t-small: 0.82rem;
    --t-label: 0.66rem;
    --t-micro: 0.58rem;

    /* mono labels share one tracking value */
    --track-label: 0.26em;

    --p: 0;
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

html {
    scroll-behavior: auto;
    /* `clip`, not `hidden` — `hidden` would make this a scroll container
       and quietly break every position:sticky on the page */
    overflow-x: clip;
}

body {
    margin: 0;
    font-family: var(--font-body);
    color: var(--fg);
    background: #05070f;
    -webkit-font-smoothing: antialiased;
}

#sky-canvas {
    position: fixed;
    inset: 0;
    width: 100%; height: 100%;
    z-index: -2;
    display: block;
    pointer-events: none;
}

#sky-fallback { display: none; }
.no-webgl #sky-canvas { display: none; }
.no-webgl #sky-fallback {
    display: block;
    position: fixed;
    inset: 0;
    z-index: -2;
    background:
        radial-gradient(80% 55% at 50% 100%, #16224a 0%, transparent 60%),
        radial-gradient(60% 40% at 80% 10%, #1b1740 0%, transparent 70%),
        linear-gradient(180deg, #05070f, #080d1c);
}

.vignette {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(130% 90% at 50% 50%, transparent 32%, rgba(0, 0, 0, 0.5) 100%);
}

::selection { background: var(--accent-deep); color: #fff; }

/* Keyboard focus. The custom cursor hides the system pointer, so a clear
   focus ring is the only way a keyboard user can tell where they are.
   :focus-visible, so it never fires on mouse clicks. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
}

/* Skip link — first thing in the tab order, hidden until focused. */
.skip-link {
    position: fixed;
    top: 0.6rem; left: 0.6rem;
    z-index: 10002;
    padding: 0.7rem 1.1rem;
    border-radius: 6px;
    background: var(--accent);
    color: #08101f;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-decoration: none;
    transform: translateY(-160%);
    transition: transform 0.25s var(--ease-out);
}
.skip-link:focus { transform: none; }

/* =========================================================
   Launch sequence — this is the loading screen
   ========================================================= */

.preloader {
    position: fixed;
    inset: 0;
    z-index: 9999;
    pointer-events: none;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 0 clamp(1.2rem, 5vw, 3.5rem) 9vh;
    transition: opacity 0.9s ease;
}
.preloader.done { opacity: 0; }

.pre-inner {
    width: min(560px, 100%);
    text-align: center;
}

.pre-mark {
    font-family: var(--font-display);
    font-size: clamp(1.6rem, 4.4vw, 2.6rem);
    font-weight: 400;
    letter-spacing: -0.01em;
    color: var(--ink-00);
    margin: 0 0 1.5rem;
    opacity: 0;
    animation: preIn 1.1s var(--ease-out) 0.25s forwards;
}

/* one hairline that fills — no chunky progress bar */
.pre-rule {
    position: relative;
    height: 1px;
    background: rgba(255, 255, 255, 0.16);
    overflow: hidden;
}
.pre-rule span {
    position: absolute;
    inset: 0 100% 0 0;
    background: var(--ink-00);
    transition: right 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

.pre-foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-top: 0.85rem;
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--ink-40);
}

.pre-count {
    font-variant-numeric: tabular-nums;
    color: var(--ink-10);
}

@keyframes preIn {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}

body.loading { overflow: hidden; height: 100vh; }
body.loading main,
body.loading .site-header,
body.loading .site-footer { opacity: 0; }

main, .site-header, .site-footer { transition: opacity 1.1s ease 0.15s; }

/* =========================================================
   Cursor — a targeting reticle that snaps to what you point at
   ========================================================= */

.cursor-dot {
    position: fixed;
    top: 0; left: 0;
    z-index: 10001;
    width: 5px; height: 5px;
    margin: -2.5px 0 0 -2.5px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 10px rgba(143, 202, 255, 1);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.25s;
}

.reticle {
    position: fixed;
    top: 0; left: 0;
    z-index: 10000;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.25s;
}

.reticle i {
    position: absolute;
    width: 9px; height: 9px;
    border: 1.5px solid var(--accent);
    box-shadow: 0 0 8px rgba(143, 202, 255, 0.85);
    transition: border-color 0.25s;
}
/* four corner brackets */
.reticle i:nth-child(1) { top: 0;    left: 0;    border-right: 0; border-bottom: 0; }
.reticle i:nth-child(2) { top: 0;    right: 0;   border-left: 0;  border-bottom: 0; }
.reticle i:nth-child(3) { bottom: 0; right: 0;   border-left: 0;  border-top: 0; }
.reticle i:nth-child(4) { bottom: 0; left: 0;    border-right: 0; border-top: 0; }

.reticle.locked i { border-color: #fff; }

.cursor-on .cursor-dot, .cursor-on .reticle { opacity: 1; }
.cursor-on, .cursor-on a, .cursor-on button { cursor: none; }

/* =========================================================
   Header
   ========================================================= */

.site-header {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 200;
    padding: 1.4rem clamp(1.2rem, 5vw, 3.5rem);
}

.site-header::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(180deg, rgba(5, 7, 15, 0.75), rgba(5, 7, 15, 0));
}

.nav-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
}

.logo {
    font-family: var(--font-mono);
    font-size: var(--t-label);
    font-weight: 400;
    letter-spacing: var(--track-label);
    text-indent: var(--track-label);
    text-transform: uppercase;
    color: var(--fg);
    text-decoration: none;
    transition: opacity 0.3s;
}
.logo:hover { opacity: 0.6; }

.nav-links {
    display: flex;
    gap: clamp(1rem, 3vw, 2.4rem);
    list-style: none;
    margin: 0; padding: 0;
}

.nav-links a {
    font-family: var(--font-mono);
    font-size: var(--t-label);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--fg-soft);
    text-decoration: none;
    position: relative;
    padding-bottom: 3px;
    transition: color 0.35s;
}
.nav-links a::after {
    content: "";
    position: absolute;
    left: 0; bottom: 0;
    width: 100%; height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.45s var(--ease-out);
}
.nav-links a:hover { color: var(--fg); }
.nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }

.scroll-rail {
    position: fixed;
    top: 0; left: 0;
    height: 2px; width: 100%;
    z-index: 250;
}
.scroll-rail i {
    display: block;
    height: 100%;
    width: calc(var(--p) * 100%);
    background: linear-gradient(90deg, var(--accent-deep), #fff);
}

/* =========================================================
   Section furniture
   ========================================================= */

.section {
    position: relative;
    padding: clamp(5rem, 11vh, 8rem) clamp(1.2rem, 5vw, 3.5rem);
    max-width: 1240px;
    margin: 0 auto;
}

.eyebrow {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.34em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 1rem;
}

/* Section titles carry the display voice. Serif, large, optically tight —
   the numeral index beside them is mono, which is the whole editorial
   device: one expressive face, one neutral, nothing else. */
.section-title {
    font-family: var(--font-display);
    font-size: var(--t-h2);
    font-weight: 400;
    line-height: 0.92;
    letter-spacing: -0.018em;
    margin: 0 0 3rem;
}
.section-title em {
    font-style: italic;
    color: var(--ink-40);
}

.reveal {
    opacity: 0;
    transform: translate3d(0, 42px, -60px);
    transition: opacity 1s var(--ease-out), transform 1.2s var(--ease-out);
}
.reveal.show { opacity: 1; transform: none; }
.reveal[data-delay="1"] { transition-delay: 0.1s; }
.reveal[data-delay="2"] { transition-delay: 0.2s; }
.reveal[data-delay="3"] { transition-delay: 0.3s; }

.split-line { display: block; overflow: hidden; }
.split-line > span {
    display: block;
    transform: translateY(105%);
    transition: transform 1.3s var(--ease-out);
}
.split-line.show > span { transform: none; }

/* =========================================================
   Hero
   ========================================================= */

/* 100vh of this is the sticky stage; the rest is the scroll budget for the
   hero to recede. Kept tight so there's no dead sky before About. */
.hero-pin { height: 155vh; position: relative; }

.hero-stage {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0 clamp(1.2rem, 5vw, 3.5rem);
    perspective: 1200px;
    /* The receding hero is pushed toward the camera by translateZ, and
       perspective magnifies it ~1.4x on the way out. That magnified box
       counts as scrollable overflow, which widened the document and shoved
       the nav off the right edge on phones. */
    overflow: clip;
}

.hero-inner {
    transform-style: preserve-3d;
    transform: translateZ(calc(var(--hero-out, 0) * 340px))
               translateY(calc(var(--hero-out, 0) * -50px));
    opacity: calc(1 - var(--hero-out, 0) * 1.2);
    will-change: transform, opacity;
    width: 100%;
    max-width: 1100px;
}

.hero-tag {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    font-family: var(--font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.28em;
    text-transform: uppercase;
    color: var(--accent);
    border: 1px solid rgba(143, 202, 255, 0.35);
    border-radius: 100px;
    padding: 0.4rem 1rem;
    margin-bottom: 2rem;
    background: rgba(143, 202, 255, 0.06);
    backdrop-filter: blur(6px);
}
.hero-tag i {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: #4ade80;
    box-shadow: 0 0 10px #4ade80;
    animation: pulseDot 1.8s ease-in-out infinite;
}

/* the satellite orbits this box, so it needs to be a positioning context */
.hero-name-wrap {
    position: relative;
    display: block;
}

/* Typewriter face. It's a wide monospace, so the size comes down and the
   tracking goes positive rather than negative. */
.hero-name {
    position: relative;
    z-index: 2;
    font-family: var(--font-display);
    font-size: var(--t-hero);
    font-weight: 400;
    line-height: 0.94;
    letter-spacing: -0.02em;
    margin: 0;
    color: var(--ink-00);
    text-shadow: 0 0 120px rgba(160, 200, 255, 0.28);
}

.hero-name .ch {
    display: inline-block;
    opacity: 0;
    transform: translate3d(0, 0.5em, -260px) rotateX(-55deg);
    transition: opacity 0.7s ease, transform 1s var(--ease-out);
}
.hero-name.lit .ch { opacity: 1; transform: none; }
.hero-name .sp { display: inline-block; width: 0.28em; }

/* JS puts this on an ellipse around the name each frame; z-index flips so
   it passes behind the letters on the far side of the orbit */
.name-sat {
    position: absolute;
    left: 0; top: 0;
    width: 46px;
    margin: -23px 0 0 -23px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.8s ease 0.6s;
}
.hero-name.lit ~ .name-sat { opacity: 1; }
.name-sat svg { width: 100%; height: auto; display: block; }

.hero-lead {
    font-family: var(--font-mono);
    font-size: clamp(0.66rem, 1.5vw, 0.8rem);
    letter-spacing: 0.34em;
    text-indent: 0.34em;
    text-transform: uppercase;
    color: var(--fg-soft);
    margin: 2.4rem 0 0;
    opacity: 0;
    transition: opacity 1s ease 0.9s;
}
.hero-name.lit ~ .hero-lead { opacity: 1; }

.scroll-cue {
    position: absolute;
    bottom: 2.4rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.7rem;
    font-family: var(--font-mono);
    font-size: 0.58rem;
    letter-spacing: 0.3em;
    text-transform: uppercase;
    color: var(--fg-faint);
    opacity: calc(1 - var(--hero-out, 0) * 2);
}
.scroll-cue i {
    display: block;
    width: 1px; height: 44px;
    background: linear-gradient(180deg, currentColor, transparent);
    animation: cueFall 2.1s ease-in-out infinite;
}
@keyframes cueFall {
    0%   { transform: scaleY(0); transform-origin: top; }
    45%  { transform: scaleY(1); transform-origin: top; }
    55%  { transform: scaleY(1); transform-origin: bottom; }
    100% { transform: scaleY(0); transform-origin: bottom; }
}

@keyframes pulseDot { 50% { opacity: 0.35; transform: scale(0.75); } }

/* =========================================================
   About — a viewport and a crew record
   ========================================================= */

.about-grid {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: clamp(2.5rem, 6vw, 5rem);
    align-items: center;
}

.porthole-wrap {
    position: relative;
    width: min(360px, 78vw);
    aspect-ratio: 1;
    margin: 0 auto;
    perspective: 900px;
}

/* A precise optical ring rather than a machined hatch: one hairline, a
   thin inner bevel, a quiet blue rim and a slow outer arc. */
.porthole {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    transform-style: preserve-3d;
    transition: transform 0.6s var(--ease-out);
    padding: 3px;
    background: linear-gradient(150deg,
        rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0.06) 38%,
        rgba(255, 255, 255, 0.02) 62%, rgba(143, 202, 255, 0.35));
    box-shadow:
        0 0 0 1px rgba(255, 255, 255, 0.10),
        0 40px 90px rgba(2, 6, 18, 0.75),
        0 0 70px rgba(59, 134, 255, 0.14);
}

.porthole-glass {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    overflow: hidden;
    background: #060b18;
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.14),
        inset 0 18px 40px rgba(0, 0, 0, 0.55);
}

.me_pic {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    filter: contrast(1.05) saturate(1.02) brightness(0.98);
}

/* a single soft specular sweep, and a vignette to seat the portrait */
.porthole-glass::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        linear-gradient(142deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.03) 22%, transparent 40%),
        radial-gradient(circle at 50% 46%, transparent 52%, rgba(4, 8, 18, 0.62) 100%);
}

/* A hairline arc orbiting outside the frame — the only moving part.
   The spin lives on the <circle>, not the <svg>: rotating a square element
   grows its bounding box by 1.41x, which counts as scrollable overflow and
   pushed the page sideways on phones. An SVG-internal transform doesn't
   touch the layout box. */
.porthole-arc {
    position: absolute;
    inset: -7.5%;
    width: 115%;
    height: 115%;
    overflow: visible;
    pointer-events: none;
}
.porthole-arc circle {
    fill: none;
    stroke: rgba(255, 255, 255, 0.32);
    stroke-width: 0.45;
    stroke-dasharray: 62 240;
    stroke-linecap: round;
    transform-box: fill-box;
    transform-origin: center;
    animation: arcSpin 26s linear infinite;
}
@keyframes arcSpin { to { transform: rotate(360deg); } }

.porthole-sweep {
    position: absolute;
    inset: -10%;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.07);
    pointer-events: none;
}

/* --- the crew record --- */
.record {
    margin: 0;
    border-top: 1px solid var(--line);
}

.record-row {
    display: grid;
    grid-template-columns: minmax(120px, 0.42fr) auto minmax(0, 1fr);
    gap: 0.9rem;
    align-items: baseline;
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--line);
    transition: background 0.35s, padding-left 0.35s var(--ease-out);
}
.record-row:hover { background: rgba(143, 202, 255, 0.05); padding-left: 0.5rem; }

.record-key {
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--ink-40);
    padding-top: 0.3em;
}

.record-sep {
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--accent);
    opacity: 0.7;
}

.record-val {
    font-size: 1rem;
    color: var(--fg);
    letter-spacing: -0.005em;
    word-break: break-word;
}
.record-val a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--line); }
.record-val a:hover { color: var(--accent); border-color: var(--accent); }

/* --- comms / social --- */
.social-card {
    display: flex;
    gap: 0.8rem;
    margin-top: 2.2rem;
}

/* clipped corners + bracket frame, to match the instrument styling */
.social-card a {
    position: relative;
    width: 58px; height: 58px;
    display: grid;
    place-items: center;
    color: var(--fg);
    clip-path: polygon(22% 0, 100% 0, 100% 78%, 78% 100%, 0 100%, 0 22%);
    background: linear-gradient(150deg, rgba(143, 202, 255, 0.14), rgba(143, 202, 255, 0.03));
    box-shadow: inset 0 0 0 1px rgba(143, 202, 255, 0.3);
    transition: transform 0.45s var(--ease-out), background 0.35s, box-shadow 0.35s;
    overflow: hidden;
}

/* sweep of light on hover */
.social-card a::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, transparent 35%, rgba(255, 255, 255, 0.5) 50%, transparent 65%);
    transform: translateX(-130%);
    transition: transform 0.7s var(--ease-out);
}
.social-card a:hover::before { transform: translateX(130%); }

.social-card a svg {
    width: 22px; height: 22px;
    fill: currentColor;
    position: relative;
    z-index: 1;
    transition: transform 0.4s var(--ease-out);
}
.social-card a svg.stroked { fill: none; stroke: currentColor; }

.social-card a:hover {
    transform: translateY(-5px);
    background: linear-gradient(150deg, rgba(59, 134, 255, 0.55), rgba(143, 202, 255, 0.2));
    box-shadow: inset 0 0 0 1px rgba(143, 202, 255, 0.85), 0 0 26px rgba(59, 134, 255, 0.45);
}
.social-card a:hover svg { transform: scale(1.12); }

/* =========================================================
   Projects — a system of planets
   ========================================================= */

.system {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    /* small row gap on purpose — each card already reserves room beneath
       itself for the readout */
    gap: 0.5rem clamp(1.5rem, 4vw, 3rem);
    padding-top: 1rem;
}

.planet-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-decoration: none;
    color: inherit;
    padding-bottom: 190px;
}

/* Stagger the orbits so it reads as a system, not a grid. Margin, not
   transform — these cards are also .reveal elements, and a transform here
   would be overwritten the moment the reveal animation runs. */
.planet-card:nth-child(2) { margin-top: 70px; }
.planet-card:nth-child(3) { margin-top: 0; }
.planet-card:nth-child(4) { margin-top: 24px; }
.planet-card:nth-child(5) { margin-top: 86px; }
.planet-card:nth-child(6) { margin-top: 8px; }

/* The planets are real geometry now, rendered in WebGL and positioned to
   whatever rect this slot reports. The slot itself is invisible — it exists
   only to reserve layout space and to be measured. */
.planet-slot {
    width: var(--size, 150px);
    aspect-ratio: 1;
    border-radius: 50%;
    pointer-events: none;
}

/* fallback if WebGL never comes up: a flat disc in the project's colours */
.no-webgl .planet-slot {
    background:
        radial-gradient(circle at 30% 26%, rgba(255,255,255,.35), transparent 46%),
        radial-gradient(circle at 74% 78%, rgba(0,0,0,.85), transparent 64%),
        var(--fallback, #2b3f6b);
    box-shadow: 0 0 40px var(--glow, rgba(90,166,255,.35));
}

.planet-name {
    margin: 1.4rem 0 0;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: #cddffa;
    text-align: center;
    transition: color 0.4s, text-shadow 0.4s;
}
.planet-card:hover .planet-name {
    color: #fff;
    text-shadow: 0 0 20px rgba(143, 202, 255, 0.8);
}

.planet-info {
    position: absolute;
    top: calc(var(--size, 150px) + 3.6rem);
    left: 50%;
    width: min(280px, 84vw);
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    pointer-events: none;
}

.planet-drop {
    width: 1px; height: 0;
    background: #fff;
    box-shadow: 0 0 8px rgba(255, 255, 255, 0.9);
    transition: height 0.36s var(--ease-out);
}
.planet-card:hover .planet-drop { height: 30px; }

.planet-rule {
    width: 0; height: 1px;
    background: #fff;
    box-shadow: 0 0 8px rgba(255, 255, 255, 0.9);
    transition: width 0.45s var(--ease-out) 0.22s;
}
.planet-card:hover .planet-rule { width: 100%; }

.planet-readout {
    margin-top: 0.9rem;
    text-align: center;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 0.4s ease 0.4s, transform 0.5s var(--ease-out) 0.4s;
}
.planet-card:hover .planet-readout { opacity: 1; transform: none; }

.planet-desc {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    line-height: 1.65;
    color: #fff;
    letter-spacing: 0.02em;
}

.planet-cta {
    display: inline-block;
    margin-top: 0.8rem;
    font-family: var(--font-mono);
    font-size: 0.58rem;
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--accent);
}
.planet-cta::after { content: " \2192"; }

/* =========================================================
   Reports — a deep space downlink

   The planets above are expressive and round; this panel is the opposite
   on purpose. Hairlines, mono, one accent. It should read as instrumen-
   tation sitting under the system it reported on, not as another display
   element competing with it.
   ========================================================= */

.dsn {
    border: 1px solid var(--line);
    border-radius: 3px;
    background:
        linear-gradient(180deg, rgba(143, 202, 255, 0.035), transparent 40%),
        rgba(5, 8, 18, 0.55);
    /* the panel sits on the star canvas; blurring what shows through keeps
       the small mono type legible over bright stars */
    backdrop-filter: blur(7px);
    -webkit-backdrop-filter: blur(7px);
    overflow: hidden;
}

/* ---- panel header ---- */
.dsn-head {
    display: flex;
    align-items: center;
    gap: clamp(0.8rem, 2.5vw, 1.6rem);
    padding: 0.95rem clamp(1rem, 2.6vw, 1.7rem);
    border-bottom: 1px solid var(--line);
}

.dsn-tag {
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--accent);
    white-space: nowrap;
}

.dsn-head-rule {
    flex: 1;
    height: 1px;
    background: var(--line);
}

.dsn-status {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fg-faint);
    white-space: nowrap;
}

.dsn-led {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent);
    animation: dsn-blink 2.4s ease-in-out infinite;
}
@keyframes dsn-blink {
    0%, 62%, 100% { opacity: 1; }
    76%           { opacity: 0.18; }
}

/* ---- one packet per row ----
   Five tracks: id, copy, trace, meta, action. The copy column is the only
   flexible one, so the instrument columns stay in a straight line down the
   panel regardless of how long a title runs. */
.dsn-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) minmax(70px, 150px) auto auto;
    align-items: center;
    gap: clamp(0.9rem, 2.6vw, 2rem);
    padding: 1.15rem clamp(1rem, 2.6vw, 1.7rem);
    border-top: 1px solid var(--line);
    text-decoration: none;
    color: inherit;
    position: relative;
    transition: background 0.35s ease;
}
.dsn-row:first-child { border-top: 0; }

/* the accent edge that marks the row you're on */
.dsn-row::before {
    content: "";
    position: absolute;
    left: 0;
    top: -1px;
    bottom: 0;
    width: 2px;
    background: var(--accent);
    transform: scaleY(0);
    transform-origin: 50% 50%;
    transition: transform 0.4s var(--ease-out);
}
.dsn-row:hover,
.dsn-row:focus-visible { background: rgba(143, 202, 255, 0.05); }
.dsn-row:hover::before,
.dsn-row:focus-visible::before { transform: scaleY(1); }
.dsn-row:focus-visible { outline: none; }

.dsn-id {
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    font-weight: 600;
    letter-spacing: 0.16em;
    color: var(--ink-80);
    transition: color 0.35s ease;
}
.dsn-row:hover .dsn-id,
.dsn-row:focus-visible .dsn-id { color: var(--accent); }

.dsn-body { display: block; min-width: 0; }

.dsn-name {
    display: block;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #cddffa;
    transition: color 0.35s, text-shadow 0.35s;
}
.dsn-row:hover .dsn-name,
.dsn-row:focus-visible .dsn-name {
    color: #fff;
    text-shadow: 0 0 20px rgba(143, 202, 255, 0.7);
}

/* The abstract is the decoded packet: it only resolves once the row has
   your attention. Grid rows animate cleanly from 0fr, and unlike a fixed
   max-height it doesn't need a guessed height per row. */
.dsn-desc {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    font-family: var(--font-body);
    font-size: 0.78rem;
    line-height: 1.6;
    color: var(--fg-soft);
    transition: grid-template-rows 0.45s var(--ease-out), opacity 0.3s ease;
}
/* the inner span is what the 0fr row clips — padding rather than margin,
   so nothing escapes the clipped box and leaves a gap when collapsed */
.dsn-desc > span {
    min-height: 0;
    overflow: hidden;
    padding-top: 0.5rem;
}
.dsn-row:hover .dsn-desc,
.dsn-row:focus-visible .dsn-desc {
    grid-template-rows: 1fr;
    opacity: 1;
    transition: grid-template-rows 0.5s var(--ease-out), opacity 0.45s ease 0.1s;
}

/* ---- the live trace ----
   An SVG polyline built in JS, drawn twice end to end and slid left by
   exactly half its width, so the loop is seamless. */
.dsn-wave {
    display: block;
    height: 22px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
.dsn-wave svg {
    display: block;
    width: 200%;
    height: 100%;
    animation: dsn-scroll 3.4s linear infinite;
}
.dsn-wave polyline {
    fill: none;
    stroke: var(--ink-80);
    stroke-width: 1.4;
    vector-effect: non-scaling-stroke;
    transition: stroke 0.35s ease;
}
.dsn-row:hover .dsn-wave polyline,
.dsn-row:focus-visible .dsn-wave polyline { stroke: var(--accent); }
.dsn-row:hover .dsn-wave svg,
.dsn-row:focus-visible .dsn-wave svg { animation-duration: 1.3s; }

@keyframes dsn-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

/* ---- signal strength + format ---- */
.dsn-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.42rem;
}

/* Both layers use the same 4-on/3-off tick geometry from the same origin,
   so the lit ticks land exactly on top of the unlit ones. */
.dsn-sig {
    width: 58px;
    height: 8px;
    background: repeating-linear-gradient(90deg, var(--line-strong) 0 4px, transparent 4px 7px);
}
.dsn-sig i {
    display: block;
    height: 100%;
    width: var(--sig, 70%);
    background: repeating-linear-gradient(90deg, var(--accent) 0 4px, transparent 4px 7px);
    opacity: 0.55;
    transition: opacity 0.35s ease, filter 0.35s ease;
}
.dsn-row:hover .dsn-sig i,
.dsn-row:focus-visible .dsn-sig i {
    opacity: 1;
    filter: drop-shadow(0 0 5px rgba(143, 202, 255, 0.8));
}

.dsn-fmt {
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--fg-faint);
    white-space: nowrap;
}

/* ---- the action ---- */
.dsn-cta {
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--accent);
    white-space: nowrap;
    opacity: 0;
    transform: translateX(-6px);
    transition: opacity 0.35s ease, transform 0.45s var(--ease-out);
}
.dsn-cta i {
    font-style: normal;
    display: inline-block;
    transition: transform 0.45s var(--ease-out);
}
.dsn-row:hover .dsn-cta,
.dsn-row:focus-visible .dsn-cta { opacity: 1; transform: none; }
.dsn-row:hover .dsn-cta i,
.dsn-row:focus-visible .dsn-cta i { transform: translateX(4px); }

/* Touch has no hover, so nothing above would ever resolve — show the
   abstract and the action outright. Laid out as a plain block rather than
   an expanded grid row: there is nothing to animate here, and an fr row
   whose child clips its own overflow renders a line short in Chrome. */
@media (hover: none) {
    .dsn-desc { display: block; opacity: 1; }
    .dsn-desc > span { display: block; overflow: visible; }
    .dsn-cta { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .dsn-wave svg { animation: none; }
    .dsn-led { animation: none; }
}

/* =========================================================
   Experience — a constellation you can swipe through
   ========================================================= */

.const-head {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(1rem, 4vw, 2.6rem);
    margin-top: 1.4rem;
}

.const-title { text-align: center; min-width: 12ch; }

.const-name {
    font-family: var(--font-display);
    font-size: clamp(1.4rem, 3vw, 2.1rem);
    font-weight: 400;
    letter-spacing: 0.01em;
    text-transform: none;
    color: var(--ink-00);
    margin: 0;
    text-shadow: 0 0 40px rgba(180, 214, 255, 0.4);
}

.const-sub {
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--fg-faint);
    margin: 0.35rem 0 0;
}

.const-arrow {
    flex: none;
    width: 42px; height: 42px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    border: 1px solid var(--line-strong);
    background: rgba(255, 255, 255, 0.05);
    backdrop-filter: blur(6px);
    color: var(--ink-10);
    font-size: 0.95rem;
    transition: background 0.3s, border-color 0.3s, transform 0.3s var(--ease-out);
}
.const-arrow:hover {
    background: rgba(59, 134, 255, 0.35);
    border-color: var(--accent);
    transform: scale(1.08);
}

.const-dots {
    display: flex;
    justify-content: center;
    gap: 0.2rem;
    margin-top: 0.9rem;
}

/* 7px of ink, but a 32px hit area — a bare 7px dot is far below the
   minimum comfortable touch target. */
.const-dots button {
    width: 32px; height: 32px;
    padding: 0;
    border: 0;
    background: none;
    display: grid;
    place-items: center;
}
.const-dots button::before {
    content: "";
    width: 7px; height: 7px;
    border-radius: 50%;
    background: rgba(233, 241, 255, 0.32);
    transition: background 0.3s, transform 0.3s var(--ease-out), box-shadow 0.3s;
}
.const-dots button[aria-current="true"]::before {
    background: var(--accent);
    box-shadow: 0 0 10px var(--accent);
    transform: scale(1.5);
}

.const-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 560px;
    touch-action: pan-y;      /* let vertical scroll through, we take X */
    cursor: grab;
    overflow: clip;
}
.const-stage:active { cursor: grabbing; }

/* The engraved plate, sitting well behind the stars. It's a white block
   masked by the line art, so the colour and opacity stay in CSS. */
.const-figure {
    position: absolute;
    left: 0; top: 0;
    pointer-events: none;
    background-color: rgba(255, 255, 255, 0.40);
    -webkit-mask-image: var(--art);
    mask-image: var(--art);
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    opacity: 1;
    transition: opacity 0.4s ease;
}

.const-svg {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    pointer-events: none;
}
.const-line {
    fill: none;
    stroke: rgba(143, 202, 255, 0.45);
    stroke-width: 1;
    stroke-linecap: round;
}
/* the constellation's other real stars */
.const-tie {
    stroke: rgba(233, 241, 255, 0.45);
    stroke-width: 1;
    stroke-dasharray: 2 3;
    transition: opacity 0.3s;
}

.const-minor-star {
    fill: rgba(214, 232, 255, 0.75);
    filter: drop-shadow(0 0 4px rgba(143, 202, 255, 0.9));
}

.star {
    position: absolute;
    left: 0; top: 0;
    width: 0; height: 0;
}

.star-hit {
    position: absolute;
    left: -22px; top: -22px;
    width: 44px; height: 44px;
    padding: 0; border: 0;
    background: none;
    border-radius: 50%;
}

.star-dot {
    position: absolute;
    left: -5px; top: -5px;
    width: 10px; height: 10px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 10px rgba(143, 202, 255, 0.9), 0 0 24px rgba(143, 202, 255, 0.5);
    transition: transform 0.4s var(--ease-out);
    pointer-events: none;
}

/* four-point twinkle */
.star-dot::before, .star-dot::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    opacity: 0;
    color: rgba(200, 226, 255, 0.9);
    transition: opacity 0.45s;
}
.star-dot::before {
    width: 1px; height: 40px;
    margin: -20px 0 0 -0.5px;
    background: linear-gradient(transparent, currentColor, transparent);
}
.star-dot::after {
    width: 40px; height: 1px;
    margin: -0.5px 0 0 -20px;
    background: linear-gradient(90deg, transparent, currentColor, transparent);
}

.star.on .star-dot { transform: scale(1.5); }
.star.on .star-dot::before, .star.on .star-dot::after { opacity: 1; }

/* the role now lives on the star itself, so nothing trails below the
   constellation */
/* JS places these: --lx/--ly are offsets from the star, chosen to avoid
   colliding with the other labels and to stay inside the stage. */
.star-label {
    position: absolute;
    left: 0; top: 0;
    width: max-content;
    max-width: 19ch;
    transform: translate(var(--lx, 16px), var(--ly, -1.5em));
    pointer-events: none;
}
.star[data-side="right"] .star-label { text-align: left; }
.star[data-side="left"]  .star-label { text-align: right; }

.star-org {
    display: block;
    font-size: 0.84rem;
    font-weight: 500;
    color: var(--fg);
    letter-spacing: -0.01em;
    line-height: 1.25;
    transition: color 0.35s;
}
.star-role {
    display: block;
    font-size: 0.7rem;
    color: var(--fg-soft);
    line-height: 1.35;
    margin-top: 0.15rem;
}
.star-when {
    display: block;
    font-family: var(--font-mono);
    font-size: 0.54rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fg-faint);
    margin-top: 0.25rem;
}
.star.on .star-org { color: #fff; text-shadow: 0 0 18px rgba(143, 202, 255, 0.8); }

/* =========================================================
   Skills
   ========================================================= */

/* tighter than the other sections: the sphere needs the vertical budget,
   and the whole block has to clear a laptop viewport */
#skills { padding-top: clamp(3.5rem, 7vh, 5.5rem); padding-bottom: clamp(3rem, 6vh, 4.5rem); }

.skills-filters {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
    /* the sphere was crowding right up under these */
    margin: -1.5rem 0 2.6rem;
}

.skills-filter {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 40px;              /* comfortable touch target */
    padding: 0.5rem 1.05rem;
    border-radius: 100px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.04);
    color: rgba(255, 255, 255, 0.7);
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    transition: all 0.35s var(--ease-out);
}
.skills-filter:hover { border-color: rgba(143, 202, 255, 0.6); color: #fff; }
.skills-filter.active { background: var(--accent); border-color: var(--accent); color: #0b1020; }

.filter-dot { width: 7px; height: 7px; border-radius: 50%; display: block; }
.filter-dot[data-cat="languages"] { background: #8fcaff; }
.filter-dot[data-cat="ai"]        { background: #b39dff; }
.filter-dot[data-cat="robotics"]  { background: #6ee7d5; }
.filter-dot[data-cat="webcloud"]  { background: #ffc98f; }

.cloud-stage {
    position: relative;
    width: 100%;
    /* sized so the title, filters, sphere and the hint below it all fit
       inside one laptop viewport without scrolling */
    height: min(500px, 52vh);
    cursor: grab;
    /* labels swing wide at the equator; without this they push the page
       sideways and the whole layout zooms out on phones */
    overflow: clip;
}
.cloud-stage:active { cursor: grabbing; }

.cloud-sphere { position: absolute; inset: 0; }

.cloud-tag {
    position: absolute;
    left: 0; top: 0;
    white-space: nowrap;
    font-family: var(--font-body);
    font-weight: 500;
    letter-spacing: -0.01em;
    will-change: transform, opacity;
    transition: color 0.4s;
    user-select: none;
    pointer-events: none;
}

.cloud-tag[data-cat="languages"] { color: #8fcaff; }
.cloud-tag[data-cat="ai"]        { color: #b39dff; }
.cloud-tag[data-cat="robotics"]  { color: #6ee7d5; }
.cloud-tag[data-cat="webcloud"]  { color: #ffc98f; }

.cloud-tag.dim { opacity: 0.1 !important; }
.cloud-tag.match { text-shadow: 0 0 22px currentColor; }

.cloud-stage.flat { height: auto; overflow: visible; cursor: default; }
.cloud-stage.flat .cloud-sphere {
    position: static;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.55rem 1rem;
}
.cloud-stage.flat .cloud-tag {
    position: static;
    transform: none !important;
    opacity: 1 !important;
    white-space: normal;
}
.cloud-stage.flat .cloud-tag.dim { opacity: 0.18 !important; }
.cloud-stage.flat + .cloud-hint { display: none; }

.cloud-hint {
    text-align: center;
    font-family: var(--font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--fg-faint);
    margin-top: 1rem;
}

/* =========================================================
   Footer
   ========================================================= */

.site-footer {
    position: relative;
    padding: 6rem clamp(1.2rem, 5vw, 3.5rem) 3rem;
    text-align: center;
}

.footer-meta {
    font-family: var(--font-mono);
    font-size: 0.65rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--fg-faint);
    margin: 0;
}

/* =========================================================
   Responsive
   ========================================================= */

@media (max-width: 1040px) {
    .system { grid-template-columns: repeat(2, 1fr); }
    .planet-card:nth-child(odd)  { margin-top: 0; }
    .planet-card:nth-child(even) { margin-top: 70px; }

    /* the trace is the one purely decorative column — it goes first */
    .dsn-wave { display: none; }
    .dsn-row  { grid-template-columns: auto minmax(0, 1fr) auto auto; }
}

/* Below this the five tracks can't share a line at a readable size, so the
   instruments drop under the copy and the row reads top to bottom. */
@media (max-width: 700px) {
    .dsn-head { flex-wrap: wrap; gap: 0.6rem 1rem; }
    .dsn-head-rule { display: none; }

    .dsn-row {
        grid-template-columns: auto minmax(0, 1fr);
        align-items: start;
        gap: 0.6rem 0.9rem;
    }
    .dsn-id { padding-top: 0.12rem; }

    .dsn-meta {
        grid-column: 2;
        flex-direction: row;
        align-items: center;
        gap: 0.7rem;
    }
    .dsn-cta { grid-column: 2; justify-self: start; }
}

@media (max-width: 900px) {
    .about-grid { grid-template-columns: 1fr; }
    .social-card { justify-content: center; }
    .nav-links { gap: 0.9rem; }
    .nav-links a { font-size: 0.6rem; letter-spacing: 0.1em; }
    .const-stage { aspect-ratio: 4 / 3; }
}

@media (max-width: 760px) {
    .hero-pin { height: 150vh; }

    .system { grid-template-columns: 1fr; gap: 2.5rem; }
    .planet-card { margin-top: 0 !important; padding-bottom: 170px; }

    /* Rings reach 2.15x the sphere's radius. At the desktop sizes they'd
       span almost the whole width of a phone, so the slot is capped in vw
       here and the readout follows it. */
    .planet-slot { width: min(var(--size, 150px), 38vw); }
    .planet-info { top: calc(min(var(--size, 150px), 38vw) + 3.2rem); }

    /* rings are wider than their planet; at one column they were the widest
       thing on the page and pushed the document past the viewport */
    .planet-ring { width: 168%; margin-left: -84%; height: 54%; margin-top: -27%; }
    .planet-ring.inner { width: 140%; margin-left: -70%; height: 44%; margin-top: -22%; }

    /* taller stage + smaller labels: five three-line labels need real
       vertical room once the constellation is only ~300px wide */
    .const-stage { aspect-ratio: 5 / 7; }
    .star-label { max-width: 14ch; }
    .star-org  { font-size: 0.7rem; line-height: 1.2; }
    .star-role { font-size: 0.58rem; line-height: 1.25; margin-top: 0.1rem; }
    .star-when { font-size: 0.46rem; margin-top: 0.14rem; }

    .record-row { grid-template-columns: 1fr; gap: 0.2rem; }
    .record-sep { display: none; }

    .star-org { font-size: 0.76rem; }
    .star-when { font-size: 0.5rem; }
    .star[data-side="right"] .star-label { left: 14px; }
    .star[data-side="left"]  .star-label { right: 14px; }

    .cloud-stage { height: 380px; }
}

@media (max-width: 360px) {
    .site-header { padding: 0.9rem 0.6rem; }
    .logo { display: none; }        /* nav wins the space at this width */
    .nav-links { gap: 0.55rem; width: 100%; justify-content: space-between; }
}

@media (max-width: 560px) {
    /* At this width the four nav labels run off the right edge — and
       the mono logo needs its tracking pulled in too. */
    .site-header { padding: 1rem 0.9rem; }
    .nav-inner { gap: 0.5rem; }
    .logo { font-size: 0.55rem; letter-spacing: 0.07em; text-indent: 0.07em; }
    .nav-links { gap: 0.5rem; }
    .nav-links a { font-size: 0.5rem; letter-spacing: 0.02em; }

    .hero-tag { font-size: 0.54rem; letter-spacing: 0.18em; }
    .name-sat { width: 32px; margin: -16px 0 0 -16px; }
}

@media (hover: none) {
    .cursor-dot, .reticle { display: none; }

    /* Nothing hovers on touch, so the readouts would never appear. */
    .planet-drop { height: 30px; }
    .planet-rule { width: 100%; }
    .planet-readout { opacity: 1; transform: none; }
    .planet-name { color: #fff; }
}

@media (prefers-reduced-motion: reduce) {
    * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
    .reveal { opacity: 1; transform: none; }
    .split-line > span { transform: none; }
    .hero-name .ch { opacity: 1; transform: none; }
    .hero-pin { height: auto; }
    .hero-stage { position: relative; height: auto; padding: 6rem 0; }
}
